
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Web.UI;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;
using Swaf.Security;
using Swaf.Gui;
using Swaf.Gui.AspNet2;

namespace Syrinx.Gui.AspNet.Nitobi2
{
	[ParseChildren(false)]
	public class ComplexBizObjListDisplay : Swaf.Gui.AspNet2.ComplexBizObjListDisplay, INitobiClientControl
	{
		protected static Dictionary<string, int> s_fieldTypeArrayIndex = new Dictionary<string,int>();
		protected XmlNodeList m_clientBizObjMods = null;
		protected bool m_hasUpdatableFieldControls = false;
		protected bool m_mySelectEvent = false;

		static ComplexBizObjListDisplay()
		{
			int pos = 0;
			s_fieldTypeArrayIndex.Add("textbox", pos++);
			s_fieldTypeArrayIndex.Add("hiddenfield", pos++);
			s_fieldTypeArrayIndex.Add("checkbox", pos++);
			s_fieldTypeArrayIndex.Add("dropdowncalendar", pos++);
			s_fieldTypeArrayIndex.Add("nitobicomboboxbizobjlistdisplay", pos++);
			s_fieldTypeArrayIndex.Add("radiobutton", pos++);
		}

		public ComplexBizObjListDisplay()
			:base()
		{
		}

		public ComplexBizObjListDisplay(ActiveWidget widget)
			:base(widget)
		{
		}

		protected override void AddParsedSubObject(object obj)
		{
			base.AddParsedSubObject(obj);

			//System.Web.UI.LiteralControl control = (System.Web.UI.LiteralControl)obj;
			//if (control.Text.Trim().Length != 0)
			//{
			//    XmlDocument doc = new XmlDocument();
			//    doc.LoadXml(control.Text);
			//    base.m_localMacroValues = new FlexiMap(doc.DocumentElement);
			//}
		}


		public override void showBizObjs(IBizObjCursor cursor)
		{
			base.showBizObjs(cursor);
		}

		public override void updateBizObjs()
		{
			processClientMods();
		}

		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);
			if(Page is INitobiPage || (Page is NitobiBasePage && ((NitobiBasePage)Page).UsingAjax))
				Page.ClientScript.RegisterClientScriptBlock(GetType(), "Global", s_globalScript, true);
		}
		protected override void  Render(System.Web.UI.HtmlTextWriter writer)
		{
			bool usingAjax = (Page is INitobiPage || (Page is NitobiBasePage && ((NitobiBasePage)Page).UsingAjax));
			if (usingAjax)
				writer.Write("<!--{0}-->", ClientID);

			base.Render(writer);

			if (usingAjax)
			{
				string parentBizProcId = null;
				ActiveBizProc bizproc = null;

				if(State.ParentBizProcIds != null && State.ParentBizProcIds.Count > 0)
					parentBizProcId = State.ParentBizProcIds[0];
				if(parentBizProcId != null)
					bizproc = BPC.getActiveBizProc(parentBizProcId);
				if (bizproc == null)
					throw new BaseException(string.Format("Cannot find parent bizproc for bizObjListDisplay {0} using parent id of {1}", WidgetId, parentBizProcId));

				writer.Write(s_setupScript, bizproc.BizProc.Id, WidgetId, base.ContentName, m_hasUpdatableFieldControls.ToString().ToLower(), base.realMaxElements());
				writer.Write("<!--{0}-->", ClientID);
			}
		}

		#region INitobiClientControl Members
		public bool NeedToGenerateHtmlWithServerControls { get { return true;/*!m_mySelectEvent;*/ } }

		public virtual void setState(XmlElement clientState)
		{
			ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
			string ajaxEvent = clientState.OwnerDocument.DocumentElement.GetAttribute("event").ToLower();
			if (clientState.OwnerDocument.DocumentElement.GetAttribute("bizProcId").CompareTo(bizproc.BizProc.Id) == 0)
			{
				if (ajaxEvent.CompareTo("bizobjselected") == 0)
					m_mySelectEvent = true;
			}
			if (ajaxEvent != null && ajaxEvent.Length != 0)
			{
				if (clientState.GetAttribute("template") != "")
					base.m_readOnlyTemplate = clientState.GetAttribute("template");
				m_clientBizObjMods = clientState.SelectNodes("BizObj");

                if (!Cmn.IsEmpty(clientState.GetAttribute("maxElements")))
                    base.MaxElements = clientState.GetAttribute("maxElements");
			}
			
		}

		public virtual void getState(System.Xml.XmlElement clientState, string pageHtml)
		{
			clientState.SetAttribute("template", base.ContentName);
            clientState.SetAttribute("maxElements", base.MaxElements.ToString());
			if (pageHtml != null)
			{
				StringBuilder scriptBuff = new StringBuilder();
				Regex r = new Regex(string.Format("<!--{0}-->(.*?)<!--{0}-->", ClientID.Replace("$", "\\$")), RegexOptions.IgnoreCase | RegexOptions.Singleline);
				Match m = r.Match(pageHtml);
				if (m.Success)
				{
					StringBuilder htmlBuff = new StringBuilder();
					string myHtml = m.Groups[1].Value;
					r = new Regex("<script.*?>(.*?)</script>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
					MatchCollection mc = r.Matches(myHtml);
					int lastPos = 0;
					foreach (Match m2 in mc)
					{
						htmlBuff.Append(myHtml.Substring(lastPos, m2.Index - lastPos));
						scriptBuff.Append(m2.Groups[1].Value);
						lastPos = m2.Index + m2.Length;
					}
					//if (lastPos != 0)
					htmlBuff.Append(myHtml.Substring(lastPos));

					clientState.SetAttribute("html", htmlBuff.ToString());
				}

				foreach (System.Web.UI.Control ctl in Controls)
				{
					Combo combo = ctl as Combo;
					if (combo != null)
					{
						r = new Regex(@"(.*)_(\d+)_(\d+)$");
						m = r.Match(ctl.ID);
						if (m.Success)
						{
							int row = Int32.Parse(m.Groups[2].Value);
							if (row >= 0 && row < m_bizObjCursor.Source.numElements)
							{
								IBizObj obj = m_bizObjCursor.Source[row];
								//scriptBuff.AppendFormat(s_nitobiComboSetupScript, clientState.GetAttribute("script"), ClientID, ctl.ID, combo.LiveScroll.ToString().ToLower(), obj.get(m.Groups[1].Value));
							}
						}
					}
				}

				clientState.SetAttribute("script", scriptBuff.ToString());
			}
		}

		protected virtual void processClientMods()
		{
			if (m_bizObjCursor != null && m_clientBizObjMods != null)
			{
				ActiveBizProc parent = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
				IBizObjList baseBizObjs = m_bizObjCursor.Source;
				if (m_selectedBizObjs == null && baseBizObjs is MasterBaseBizObjList)
					m_selectedBizObjs = ((MasterBaseBizObjList)baseBizObjs).BaseList;
				IBizObjList modifiedBizObjs = App.bizObjMgr.createList(baseBizObjs.objectType.Name);
				IBizObjList unselectedBizObjs = null;
				IBizObjList selectedBizObjs = null;
				string keyField = baseBizObjs.objectType.KeyFieldNames;
				IBizObj baseObj = null;
				int index = 0;
				foreach (XmlElement bizObjEl in m_clientBizObjMods)
					if (keyField.Length == 0 || bizObjEl.HasAttribute(keyField))
					{
						try
						{
							if (keyField.Length == 0)
								baseObj = baseBizObjs[index++];
							else
								baseObj = baseBizObjs.search(keyField, modifiedBizObjs.objectType.convertValueToKeyType(bizObjEl.GetAttribute(keyField)));
						}
						catch (Exception e)
						{
							//TODO: Log exception about not being able to search for bizobj due to not having a good key value to search with.
						}
						bool changed = false;
						if (baseObj != null)
						{
							foreach (XmlAttribute att in bizObjEl.Attributes)
								try
								{
									if (att.Name[0] != '_')
									{
										if (string.Compare(att.Value, baseObj.get(att.Name).ToString()) != 0)
										{
											changed = true;
											baseObj.put(att.Name, att.Value);
										}
									}
									else if (att.Name.CompareTo("_select") == 0)
									{
										bool isSelected = false;
										bool.TryParse(att.Value, out isSelected);
										if (isSelected)
										{
											if (m_selectedBizObjs == null || (m_selectedBizObjs != null && m_selectedBizObjs.search(baseObj.type.KeyFieldNames, baseObj.KeyValue) == null))
											{
												if (selectedBizObjs == null)
													selectedBizObjs = App.bizObjMgr.createList(baseObj.type.Name);
												selectedBizObjs.Add(baseObj);
											}
										}
										else
										{
											if (m_selectedBizObjs != null && m_selectedBizObjs.search(baseObj.type.KeyFieldNames, baseObj.KeyValue) != null)
											{
												if (unselectedBizObjs == null)
													unselectedBizObjs = App.bizObjMgr.createList(baseObj.type.Name);
												unselectedBizObjs.Add(baseObj);
											}
										}
									}
								}
								catch (Exception e)
								{
									//TODO: Log exception about bad put value.  This is where a validation error
									//can occur or if a attribute name is not a valid bizobj field.
								}
						}
						else
						{
							//TODO: Log the fact that a bizobj key value is not present in the bizobj list now.
							//This could happen because a different user deleted the bizobj.
						}
						if (changed)
						{
							modifiedBizObjs.Add(baseObj);
							parent.BizProc.updateBizObj(baseObj);
						}
					}
				if (unselectedBizObjs != null || selectedBizObjs != null)
					parent.BizProc.bizObjCheckedChanged(unselectedBizObjs, selectedBizObjs);
				m_clientBizObjMods = null;
			}
		}

		public void processAjaxEvent(System.Xml.XmlElement eventInfo)
		{
			string eventType = eventInfo.GetAttribute("event");
			if ("bizObjSelected".CompareTo(eventType) == 0)
			{
				string bizObjId = eventInfo.GetAttribute("value");
				ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
				bizproc.BizProc.selectBizObj(bizObjId,this);
			}
		}

		public bool CreateOnLoad { set { m_createActiveOnLoad = value; } }
		#endregion

		protected IApplication App { get { return Application.currentApp; } }

		protected override bool processControl(IBizObj obj, System.Web.UI.Control ctl, string fldName, ref string fldValue)
		{
			bool isit = base.processControl(obj, ctl, fldName, ref fldValue);
			if(isit)
			{
				int index = -1;
				string ctlType = ctl.GetType().Name.ToLower();
				if (s_fieldTypeArrayIndex.TryGetValue(ctlType, out index))
				{
					ctl.ID = new StringBuilder(100).Append(ctl.ID).Append("_").Append(index).ToString();
					if(ctlType.CompareTo("hiddenfield") != 0)
						m_hasUpdatableFieldControls = true;

				}
			}
			return isit;
		}
		protected internal static string s_nitobiComboSetupScript = "{0}var c = nitobi.loadComponent('{1}_{2}_cm');var ds =c.GetList().GetXmlDataSource();if({3})c.GetList().GetPage(0, 0);setSelectedRowByKey(c,'{4}');";

		protected static string s_setupScript = @"
<script>
var cc = new ClientControl(document.getElementById('{1}'), setCpxBizObjListState, getCpxBizObjListState);
{0}BizProc.addClientControl(cc);
cc.template = '{2}';
cc.getBizObjState = boL_getBizObjState;
cc.getBizObjElement = boL_getBizObjElement;
cc.prCh = boL_prCh;
cc.isUpdateable = {3};
cc.maxElements = {4};
</script>
";
		protected static string s_globalScript = @"
function boL_getBizObjState(stateEl)
{
	this.prCh(this.dhtmlObj,stateEl);
}

function boL_prCh(el,stateEl)
{
	var idPat = /(.*)_(\d+)_(\d+)$/;
	var cs = el.childNodes;
	for(var i = 0; i < cs.length; i++)
	{
		var c = cs[i];
		var id = c.id;
		if(id != null && id.match(idPat) != null)
		{
			var fieldName = findUniquePart(RegExp.$1,RegExp.$1);
			var row = RegExp.$2;
			var typeIndex = RegExp.$3;
			var bizObj = this.getBizObjElement(stateEl, row);
			try{
			bizObj.setAttribute(fieldName, BizObjFieldTypes[typeIndex].script(c));
			}catch(e){}
		}
		else
			this.prCh(c,stateEl);
	}
}

function findUniquePart(fullId,shorterId)
{
try
{
	var pos = shorterId.lastIndexOf('_');
	if(pos != -1)
	{
		var shorterId = shorterId.substr(0, pos);
		if(nitobi.html.getElement(shorterId) != null)
			return fullId.substr(pos+1);
		else
			return findUniquePart(fullId,shorterId);
	}
	return fullId;
}
catch(err)
{
var x = err;
debugger;
}
}

function setCpxBizObjListState(stateEl)
{
	this.template = stateEl.getAttribute('template');
	var html = stateEl.getAttribute('html');
	if(html != null && html != '')
		this.dhtmlObj.innerHTML = html;

	var script = stateEl.getAttribute('script');
	eval(script);
}

function getCpxBizObjListState(stateEl)
{
	stateEl.setAttribute('template', this.template);
    stateEl.setAttribute('maxElements', this.maxElements);
	if(this.isUpdateable)
		this.getBizObjState(stateEl);
}

function boL_getBizObjElement(stateEl, rowNum)
{
	var bizObj = stateEl.selectSingleNode('BizObj[@_row = \'' + rowNum + '\']');
	if(bizObj == null)
	{
		bizObj = stateEl.ownerDocument.createElement('BizObj');
		bizObj.setAttribute('_row', rowNum);
		stateEl.appendChild(bizObj);
	}
	return bizObj;
}

var BizObjFieldTypes = [
	{type: 'textbox', script: function(dhtmlEl) {return dhtmlEl.value;} },
	{type: 'hiddenfield', script: function(dhtmlEl) {return dhtmlEl.value;} },
	{type: 'checkbox', script: function(dhtmlEl) {return dhtmlEl.checked?'True':'False';} },
	{type: 'dropdowncalendar', script: function(dhtmlEl) {return nitobi.html.getElement(dhtmlEl.id + 'Fld').value;} },
	{type: 'nitobicomboboxbizobjlistdisplay', script: function(dhtmlEl) {return getSelectedRowKey(nitobi.getComponent(dhtmlEl.id + '_cm'));} },
	{type: 'radiobutton', script: function(dhtmlEl) {return dhtmlEl.checked?'True':'False';} }
];
";
	}
}
